package com.xxwu.messagequeue.mqserver.core;

import com.xxwu.messagequeue.common.MqException;

public class Router {
    public boolean checkBindingKey(String bindingKey){
        // 空字符串的情况下，对于 direct/fanout 情况下，bindingKey 都用不上
        if(bindingKey == null){
            return true;
        }
        // 以'.'分割 可以是数字、字母、字母下划线
        // 分割的中间部分可以由通配符#、*代替
        for(int i = 0; i < bindingKey.length(); i++){
            char ch = bindingKey.charAt(i);
            if(ch >= '0' && ch <= '9'){
                continue;
            }
            if(ch >= 'A' && ch <= 'Z'){
                continue;
            }
            if(ch >= 'a' && ch <= 'z'){
                continue;
            }
            if(ch == '_' || ch == '.'){
                continue;
            }
            if(ch == '*' || ch == '#'){
                continue;
            }
            // 若以上几种都不是
            return false;
        }
        // 看分隔符分开的区域是否合法
        // 对于原始分割符 '.' 这在字符串里是特殊字符，所以需要使用 / 进行转义
        // 但 / 也是特殊字符，所以再额外需要一个 / 进行转义
        String[] split = bindingKey.split("\\.");
        for(String s : split){
            if(s.length() > 1 && (s.contains("*") || s.contains("#"))){
                return false;
            }
        }
        // 排除几种复杂的特殊情况
        // 1. aaa.#.#.bbb    => 非法
        // 2. aaa.#.*.bbb    => 非法
        // 3. aaa.*.#.bbb    => 非法
        // 4. aaa.*.*.bbb    => 合法
        for(int i = 0; i < bindingKey.length() - 1; i++){
            // 连着的 #
            if(bindingKey.charAt(i) == '#' && bindingKey.charAt(i + 1) == '#'){
                return false;
            }
            // 先 '*' 后  '#'
            if(bindingKey.charAt(i) == '*' && bindingKey.charAt(i + 1) == '#'){
                return false;
            }
            // 先 '#' 后  '*'
            if(bindingKey.charAt(i) == '#' && bindingKey.charAt(i + 1) == '*'){
                return true;
            }
        }
        return true;
    }

    public boolean checkRoutingKey(String routingKey){
        // 空字符串情况，fanout 情况下，routingKey 用不上可以为空
        if(routingKey == null || routingKey.isEmpty()){
            return true;
        }
        // 以'.'分割 可以是数字、字母、字母下划线
        for(int i = 0; i < routingKey.length(); i++){
            char ch = routingKey.charAt(i);
            if(ch >= '0' && ch <= '9'){
                continue;
            }
            if(ch >= 'A' && ch <= 'Z'){
                continue;
            }
            if(ch >= 'a' && ch <= 'z'){
                continue;
            }
            if(ch == '_' || ch == '.'){
                continue;
            }
            // 若以上几种都不是
            return false;
        }
        return true;
    }

    public boolean route(ExchangeType type, Binding binding, Message message) throws MqException {
        if(type == ExchangeType.Fanout){
            // 若这里是扇出交换机则无视bindingKey和routingKey规则直接转发
            return true;
        } else if(type == ExchangeType.Topic){
            // 根据bindingKey和routingKey是否匹配决定
            return routeTopic(binding, message);
        } else {
            // 这种情况理论上不能存在，若存在则视为异常
            throw new MqException("交换机类型错误！type=" +type);
        }
    }

    // bindingKey 和 routingKey 的匹配规则
    public boolean routeTopic(Binding binding, Message message) {
        // 根据 '.' 将两个字符串分割
        String[] bindingTokens = binding.getBindingKey().split("\\.");
        String[] routingTokens = message.getRoutingKey().split("\\.");

        int indexBinding = 0;
        int indexRouting = 0;
        while(indexBinding < bindingTokens.length && indexRouting < routingTokens.length){
            // 情况二：遇到 bindingKey 为 '*' ,'*' 可以匹配任意字符
            if(bindingTokens[indexBinding].equals("*")){
                indexBinding++;
                indexRouting++;
                // 情况三：遇到 bindingKey 为 '#'
            } else if(bindingTokens[indexBinding].equals("#")){
                indexBinding++;
                // (1)如果 # 后面没有内容了，那么直接返回 true
                if(indexBinding == bindingTokens.length){
                    return true;
                } else {
                    // (2) # 后面还有内容，拿着这个内容在 routingTokens 中找，找到后继续匹配
                    // 若找不到直接返回 false
                    indexRouting = findMatch(routingTokens, indexRouting, bindingTokens[indexBinding]);
                    // 1)没找到
                    if(indexRouting == -1){
                        return false;
                        // 2)找到了,继续匹配
                    } else{
                        indexRouting++;
                        indexBinding++;
                    }
                }
            } else {
                // 情况一：遇到普通字符串
                if(bindingTokens[indexBinding].equals(routingTokens[indexRouting])){
                    indexRouting++;
                    indexBinding++;
                } else {
                    return false;
                }
            }
            // 一个走完了，一个没走完，这种情况也不匹配
            if(indexRouting == routingTokens.length && indexBinding == bindingTokens.length){
                return true;
            }
        }
        return false;
    }

    private int findMatch(String[] routingTokens, int indexRouting, String bindingToken) {
        for(int i = indexRouting; i < routingTokens.length; i++){
            if(routingTokens[i].equals(bindingToken)){
                return i;
            }
        }
        return -1;
    }
}
